package org.ainlolcat.ainscience.statistics.handlers;

import org.ainlolcat.ainscience.UI.renderer.LineDataRenderer;
import org.ainlolcat.ainscience.UI.renderer.LineRendererSource;
import org.ainlolcat.ainscience.utils.ArraysUtil;
import org.ainlolcat.ainscience.column.Column;
import org.ainlolcat.ainscience.entity.Entity;
import org.ainlolcat.ainscience.statistics.StatisticHandler;
import org.ainlolcat.ainscience.statistics.StatisticStorage;
import org.ainlolcat.ainscience.tools;
import org.ainlolcat.ainscience.utils.XMLUtils;
import org.jdom.Element;
import org.math.plot.Plot2DPanel;

import javax.swing.*;
import java.awt.*;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.util.*;
import java.util.List;

/**
 * @author ainlolcat
 * Date: 11/5/11
 */
public class SplitIntegralStatisticHandler implements StatisticHandler, LineRendererSource{
    Hashtable<String, double[][]> data = new Hashtable<String, double[][]>();
    int BASE_LINE_DEFAULT = 30;
    int MEAN_DOTS_DEFAULT = 5;

    public JPanel getRenderer() {
        return new LineDataRenderer(this);
    }

    public Hashtable<String,String> getStatistic(Entity entity, Integer columnID) {
        return getStatistic(entity, columnID, null);
    }

    public Hashtable<String, String> getStatistic(Entity entity, Integer columnID, Element params) {
        Hashtable<String,String> stats = new Hashtable<String, String>();
        int baseLineDots = XMLUtils.getSafeInteger4Attribute(params, "baseLineDots", BASE_LINE_DEFAULT);
        int mean = XMLUtils.getSafeInteger4Attribute(params,"mean", MEAN_DOTS_DEFAULT);
        Column cy = entity.getColumnByOrder(columnID);
        if (cy.isNumeric()){
            Column cx = entity.getXColumn(cy.getAxis());
            if (cx.isNumeric()){
                double[] x = cx.getDoubleRepresentation();
                double[] y = cy.getDoubleRepresentation();
                if (params == null || params.getChildren("range").isEmpty()){
                    double start = x[0];
                    double end =  x[x.length-1];
                    String name = "Integral " + start + " " + end;
                    double peakCenter = ArraysUtil.peakCenterSmooth(x, y, baseLineDots, mean);
                    Double answerLess = integrate(
                            x,
                            y,
                            baseLineDots,
                            name,
                            new LessHalfCondition(peakCenter));
                    Double answerMore = integrate(
                            x,
                            y,
                            baseLineDots,
                            name,
                            new MoreHalfCondition(peakCenter));
                    StatisticStorage.getInstance().registerHandler(entity,name+" si less",this);
                    StatisticStorage.getInstance().registerHandler(entity,name+" si more",this);
                    StatisticStorage.getInstance().registerHandler(entity,name+" si center",this);
                    stats.put(name + " si less", answerLess.toString());
                    stats.put(name + " si more", answerMore.toString());
                    stats.put(name + " si center", Double.toString(peakCenter));
                }
                for (Element integral:(List<Element>)params.getChildren("range")){
                    //try retrieve settings from params
                    double start = XMLUtils.getSafeDouble4Attribute(integral, "start", x[0]);
                    double end = XMLUtils.getSafeDouble4Attribute(integral, "end", x[x.length-1]);
                    String name = XMLUtils.getSafeString4Attribute(integral, "name", "Integral " + start + " " + end);

                    double[][] cutted = ArraysUtil.cut(x, y, start, end);

                    double peakCenter = ArraysUtil.peakCenterSmooth(cutted[0], cutted[1], baseLineDots, mean);
                    Double answerLess = integrate(
                            cutted[0],
                            cutted[1],
                            baseLineDots,
                            name,
                            new LessHalfCondition(peakCenter));
                    Double answerMore = integrate(
                            cutted[0],
                            cutted[1],
                            baseLineDots,
                            name,
                            new MoreHalfCondition(peakCenter));
                    StatisticStorage.getInstance().registerHandler(entity,name+" si less",this);
                    StatisticStorage.getInstance().registerHandler(entity,name+" si more",this);
                    StatisticStorage.getInstance().registerHandler(entity,name+" si center",this);
                    stats.put(name + " si less", answerLess.toString());
                    stats.put(name + " si more", answerMore.toString());
                    stats.put(name + " si center", Double.toString(peakCenter));
                }
            }
        }
        return stats;
    }

    private double integrate(double[] x, double[] y, int baseLineDots , String name, Condition condition){
        //important!!!
        if (baseLineDots > x.length/6){
            baseLineDots = x.length/6;
        }
        double y0 = 0,x0 = 0,y1 = 0,x1 = 0;

        for (int i=0;i<baseLineDots;i++){
            x0+=x[i];
            y0+=y[i];
            x1+=x[x.length-1-i];
            y1+=y[y.length-1-i];
        }
        x0 = x0/baseLineDots;
        y0 = y0/baseLineDots;
        x1 = x1/baseLineDots;
        y1 = y1/baseLineDots;
        // y = y0 + (x-x0)*(y1-y0)/(x1-x0)
        double k = (y1-y0)/(x1-x0);

        double[][] data = new double[3][x.length];
        if (this.data.get(name)!=null)
            data = this.data.get(name);
        else
            this.data.put(name,data);
        int idx = 0;
        for (int j=0; j<x.length; j++){
            data[0][j] = x[j];
            data[1][j] = y[j] - y0;
            if (condition.isInside(x[j])){
                double base = 0;
                for (int i = j + 1 ; i<x.length ; i++,j++){
                    if (condition.isInside(x[i])){
                        data[0][i] = x[i];
                        data[1][i] = y[i] - (y0 + k * (x[i] - x0));
                        base = base + ((data[1][i]+data[1][i-1])/2)*(data[0][i]-data[0][i-1]);
                        data[2][i] = base;
                        idx = i;
                    }else{
                        break;
                    }
                }
                break;
            }                
        }
        return data[2][idx];
    }

    public Collection<String> getNames() {
        return data.keySet();
    }

    public double[][] getData(String name) {
        return data.get(name);
    }

    interface Condition{
        boolean isInside(double x);
    }

    class NoCondition implements Condition{

        public boolean isInside(double x) {
            return true;
        }
    }

    class MoreHalfCondition implements Condition{
        private double peakCenter;
        public MoreHalfCondition(double x){
            peakCenter = x;
        }

        public boolean isInside(double x) {
            return x>=peakCenter;
        }
    }

    class LessHalfCondition implements Condition{
        private double peakCenter;
        public LessHalfCondition(double x){
            peakCenter = x;
        }

        public boolean isInside(double x) {
            return x<=peakCenter;
        }
    }
}
